package com.cy.dda.service.comm.impl;

import java.io.*;
import java.net.URL;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Collections;
import java.util.Comparator;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.UUID;

import com.aliyun.oss.model.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;

import com.aliyun.oss.HttpMethod;
import com.aliyun.oss.OSSClient;
import com.cy.dda.comm.constant.Constants;
import com.cy.dda.comm.listener.PutObjectProgressListener;
import com.cy.dda.comm.utils.CommonUtils;
import com.cy.dda.model.Result;
import com.cy.dda.service.BaseService;
import com.cy.dda.service.comm.FileUploadService;

import lombok.extern.slf4j.Slf4j;

/**
 * Oss上传文件service
 *
 * @author: tanyilin
 * @create: 2018年10月29日 下午3:36:19
 */
@Slf4j
@Service
public class OssFileUploadServiceImpl extends BaseService implements FileUploadService{
	
	@Autowired
	private OSSClient ossClient;

	@Override
	public Result uploadImage(MultipartFile multipartFile) {
		long fileLength = multipartFile.getSize();
		if(fileLength > Constants.MAX_FILE_SIZE.longValue()) {
			return Result.fail("图片大小不可超过" + Constants.MAX_FILE_SIZE_MB + "MB");
		}
		
		boolean flag = false;
		String key = null;
		String bucketName = Constants.BUCKETNAME;
		try {
			InputStream inputStream = multipartFile.getInputStream();
			String fileName = multipartFile.getOriginalFilename();
			String suffix = fileName.substring(fileName.lastIndexOf("."));
			key = "image/".concat(UUID.randomUUID().toString()).concat(suffix);
			log.info("OSS 上传图片 uploadImage:[{}]-->[{}]", fileName, key);
			log.info("图片大小为：{} ", fileLength/(1024 * 1024) == 0 ? fileLength/1024 + "Kb" : fileLength/(1024 * 1024) + "Mb");
			ossClient.putObject(bucketName, key, inputStream);
			flag = true;
		} catch (Exception e) {
			log.error("uploadImage error", e);
		}
		
		if(!flag) {
			return Result.fail("上传图片文件失败");
		}
		
		Map<String, String> map = new HashMap<>();
		map.put("path", key);
		map.put("src", getFileUrl(key));
		
		return Result.success("上传图片文件成功", map);
	}

	@Override
	public Result uploadVideo(MultipartFile multipartFile) {
		long fileLength = multipartFile.getSize();
		if(fileLength > Constants.MAX_FILE_SIZE.longValue()) {
			return Result.fail("视频大小不可超过" + Constants.MAX_FILE_SIZE_MB + "MB");
		}
		boolean flag = false;
		String key = null;
		String bucketName = Constants.BUCKETNAME;
		//File file = null;
		try {
			InputStream inputStream = multipartFile.getInputStream();
			String fileName = multipartFile.getOriginalFilename();
			String suffix = fileName.substring(fileName.lastIndexOf("."));
			key = "video/".concat(UUID.randomUUID().toString()).concat(suffix);
			log.info("OSS 上传视频 uploadVideo:[{}]-->[{}]", fileName, key);
			log.info("视频大小为：{} ", fileLength/(1024 * 1024) == 0 ? fileLength/1024 + "Kb" : fileLength/(1024 * 1024) + "Mb");
			ossClient.putObject(bucketName, key, inputStream);
			// MultipartFile 转 File
//			file = File.createTempFile(UUID.randomUUID().toString(), suffix);
//			multipartFile.transferTo(file);
//			ossClient.putObject(new PutObjectRequest(bucketName, key, file).<PutObjectRequest>withProgressListener(new PutObjectProgressListener()));
			flag = true;
		} catch (Exception e) {
			log.error("uploadVideo error", e);
		} finally {
			//file.deleteOnExit();
		}
		
		if(!flag) {
			return Result.fail("上传视频失败");
		}
		
		Map<String, String> map = new HashMap<>();
		map.put("path", key);
		map.put("src", getFileUrl(key));
		
		return Result.success("上传视频成功", map);
	}

    @Override
    public Result uploadMP3(MultipartFile multipartFile) {

		long fileLength = multipartFile.getSize();
		if(fileLength > Constants.MAX_FILE_SIZE.longValue()) {
			return Result.fail("音频大小不可超过" + Constants.MAX_FILE_SIZE_MB + "MB");
		}

		boolean flag = false;
		String key = null;
		String bucketName = Constants.BUCKETNAME;

		try {
			InputStream inputStream = multipartFile.getInputStream();
			String fileName = multipartFile.getOriginalFilename();
			String suffix = fileName.substring(fileName.lastIndexOf("."));
			key = "mp3/".concat(UUID.randomUUID().toString()).concat(suffix);
			log.info("OSS 上传音频 uploadMP3:[{}]-->[{}]", fileName, key);
			log.info("音频大小为：{} ", fileLength/(1024 * 1024) == 0 ? fileLength/1024 + "Kb" : fileLength/(1024 * 1024) + "Mb");
			ossClient.putObject(bucketName, key, inputStream);
			flag = true;
		} catch (Exception e) {
			log.error("uploadMP3 error", e);
		}

		if(!flag) {
			return Result.fail("上传音频文件失败");
		}

		Map<String, String> map = new HashMap<>();
		map.put("path", key);
		map.put("src", getFileUrl(key));

		return Result.success("上传音频文件成功", map);
    }

    @Override
	public String getFileUrl(String key) {
		URL url = null;
		Date expiration = CommonUtils.addTime(new Date(), 2, Calendar.HOUR_OF_DAY);
		String bucketName = Constants.BUCKETNAME;
		String suffix = key.substring(key.lastIndexOf("."));
		if(Constants.IMAGE_SUFFIX.contains(suffix)) {
			// 如果是可裁剪、缩放的图片
			// 设置图片处理样式。
			String style = "image/resize,p_50";
			GeneratePresignedUrlRequest req = new GeneratePresignedUrlRequest(bucketName, key, HttpMethod.GET);
			req.setExpiration(expiration);
			req.setProcess(style);
			url = ossClient.generatePresignedUrl(req);
			
		}else {
			url = ossClient.generatePresignedUrl(bucketName, key, expiration);
		}
		return url.toString();
	}

    @Override
	public Result multipartUploadVideo(MultipartFile multipartFile) {
		long fileLength = multipartFile.getSize();
		if(fileLength > Constants.MAX_FILE_SIZE.longValue()) {
			return Result.fail("视频大小不可超过" + Constants.MAX_FILE_SIZE_MB + "MB");
		}
		boolean flag = false;
		String key = null;
		String bucketName = Constants.BUCKETNAME;
		
		try {
			
			String fileName = multipartFile.getOriginalFilename();
			String suffix = fileName.substring(fileName.lastIndexOf("."));
			key = "video/".concat(UUID.randomUUID().toString()).concat(suffix);
			log.info("OSS upload:[{}]-->[{}]", fileName, key);
			
			//步骤1：初始化一个分片上传事件。
			InitiateMultipartUploadRequest request = new InitiateMultipartUploadRequest(bucketName, key);
			InitiateMultipartUploadResult result = ossClient.initiateMultipartUpload(request);
			//返回uploadId，它是分片上传事件的唯一标识，您可以根据这个ID来发起相关的操作，如取消分片上传、查询分片上传等。
			String uploadId = result.getUploadId();
			
			//步骤2：上传分片。
			//partETags 是PartETag的集合。PartETag由分片的ETag和分片号组成。
			List<PartETag> partETags =  new ArrayList<PartETag>();
			//计算文件有多少个分片。
			// 1MB
			final long partSize = 1 * 1024 * 1024L;  
			int partCount = (int) (fileLength / partSize);
			if (fileLength % partSize != 0) {
			    partCount++;
			 }
			// 遍历分片上传。
			for(int i = 0; i < partCount; i++) {
				long startPos = i * partSize;
				long curPartSize = (i + 1 == partCount) ? (fileLength - startPos) : partSize;
				InputStream inputStream = multipartFile.getInputStream();
				// 跳过已经上传的分片。
				inputStream.skip(startPos);
				// 分片上传请求
				UploadPartRequest uploadPartRequest = new UploadPartRequest(); 
				uploadPartRequest.setBucketName(bucketName);
				uploadPartRequest.setKey(key);
				uploadPartRequest.setUploadId(uploadId);
				uploadPartRequest.setInputStream(inputStream);
				// 设置分片大小。除了最后一个分片没有大小限制，其他的分片最小为100KB。
				uploadPartRequest.setPartSize(curPartSize);
				// 设置分片号。每一个上传的分片都有一个分片号，取值范围是1~10000，如果超出这个范围，OSS将返回InvalidArgument的错误码。
				uploadPartRequest.setPartNumber(i + 1);
				// 每个分片不需要按顺序上传，甚至可以在不同客户端上传，OSS会按照分片号排序组成完整的文件。
				UploadPartResult uploadPartResult = ossClient.uploadPart(uploadPartRequest);
				// 每次上传分片之后，OSS的返回结果会包含一个PartETag。PartETag将被保存到partETags中。
				partETags.add(uploadPartResult.getPartETag());
			}
			
			//步骤3：完成分片上传。
			//排序。partETags必须按分片号升序排列。
			Collections.sort(partETags, new Comparator<PartETag>() {
				@Override
				public int compare(PartETag p1, PartETag p2) {
					return p1.getPartNumber() - p2.getPartNumber();
				}
			});
			
			//在执行该操作时，需要提供所有有效的partETags。OSS收到提交的partETags后，会逐一验证每个分片的有效性。当所有的数据分片验证通过后，OSS将把这些分片组合成一个完整的文件。
			CompleteMultipartUploadRequest completeMultipartUploadRequest = new CompleteMultipartUploadRequest(bucketName, key, uploadId, partETags); 
			ossClient.completeMultipartUpload(completeMultipartUploadRequest);
			
			//上传成功
			flag = true;
			
		} catch (Exception e) {
			log.error("uploadVideo error", e);
		} 
		
		return flag ? Result.success("上传成功", key) : Result.fail("上传失败");
	}



}
